package com.matthewmadson.util.cli;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.List;

import com.google.common.collect.Lists;
import com.matthewmadson.util.regex.RegexTokenizer;
import com.matthewmadson.util.regex.Token;

/**
 * 
 * @author Matthew Madson
 */
public final class CLIBuilder
{
	private static final String EMPTY_STRING = "";
	private static final String EOL = System.getProperty("line.separator");
	
	private final String CHAR_ENCODING;
	private final int MAX_LINE_LENGTH;
	private BufferedReader in;
	private Writer out; 
	
	public CLIBuilder() 
	{
		this("UTF-8", 80);
	}
	
	public CLIBuilder(final String characterEncoding, final int maxLineLength) 
	{
		this.CHAR_ENCODING = characterEncoding;
		this.MAX_LINE_LENGTH = maxLineLength;
		
		try
		{
			this.in = new BufferedReader(new InputStreamReader(System.in,
				this.CHAR_ENCODING));
			this.out = new OutputStreamWriter(System.out, this.CHAR_ENCODING);
		}
		catch(UnsupportedEncodingException e)
		{
			throw new RuntimeException(e);
		}
	}

	private void write(final String message)
	{
		RegexTokenizer whitespaceSplitter = new RegexTokenizer(message.replace("\t", "    "), "\\S+", Boolean.TRUE);
		StringBuilder lineBuilder = new StringBuilder(this.MAX_LINE_LENGTH);
		List<String> lines = Lists.newArrayList();
		try
		{	
			for(Token t : whitespaceSplitter.getTokens())
			{			
				String toInsert = t.getText();
				if(toInsert.length() >this. MAX_LINE_LENGTH)
				{
					if(t.isDelimiter())
						toInsert = " ";
					else if(t.isToken())
						toInsert = toInsert.substring(0, this.MAX_LINE_LENGTH-4)+"...";
				}
				if(toInsert.length()+lineBuilder.length() > this.MAX_LINE_LENGTH)
				{
					lines.add(lineBuilder.toString()+EOL);
					lineBuilder.setLength(0);
					lineBuilder.append(toInsert);
				}
				else
				{
					lineBuilder.append(toInsert);
				}
			}
			
			if(lineBuilder.length() > 0)
				lines.add(lineBuilder.toString());
			
			for(String line : lines)
				this.out.write(line);
			
			this.out.flush();
		}
		catch(IOException e)
		{
			// TODO: Log this error
		}
	}

	private void writeln(final String message)
	{
		write(message + EOL);
	}
	
	public final void inform(final String message)
	{
		writeln("<< "+message+" >>");
	}
		
	public final String getString(Constraint ... constraints)
	{
		return getString("Please enter text:",constraints);
	}
	
	public final String getString(final String prompt, final Constraint ... constraints)
	{
		String result = null;
		boolean constraintsMet = false;
		while(!constraintsMet)
		{
			writeln(prompt);
			write(">> ");
			try
			{
				result = this.in.readLine();
			}
			catch(IOException e)
			{
				writeln("An error occurred, please try again");
			}
			
			constraintsMet = true;
			for(Constraint c : constraints)
				switch(c)
				{
					case NOT_NULL:
						if(result == null)
							constraintsMet = false;
						break;
					case NOT_EMPTY:
						if(result.equals(EMPTY_STRING))
							constraintsMet = false;
						break;
					default:
						break;
				}
		}
		return result;
	}
}
